<?php
/**
 * minf - core library
 * modules/application.php
 *
 * @author per function || minf.org team
 * @version 0.beta4
 * @copyright no,no,left, minf.org, 25 February, 2009
 * @license GPL
 * @package default
 **/

/**
 * application manager class
 **/
abstract class App {
	public static $Apps;
	public static $Tpls;
	public static $Calls;
	public static $Targets;
	public static $Methods;
	public static $Returns;
	public static $Paths;
	private static $BlockedMethodNames;
	private static $DoNotRun;

	/**
	 * Initializes Class
	 */
	public static function GetSelfMethods() {
		// Get Methods of this class
		$selfmthds = get_class_methods("App");
		// and add them to blocked method names (for applications)
		foreach ($selfmthds as $mtd)
			self::$BlockedMethodNames[] = $mtd;
	}

	/**
	 * Function to scan and Register Applications
	 */
	public static function Scan($p) {
		self::GetSelfMethods();
		if (substr($p,-1)!="/") $p .= "/";
		$list = dirList($p);
		if (!is_array($list)) $list = array();
		foreach ($list as $appfile) {
			if (is_dir($p.$appfile)) {
				$state = self::RegisterBundle($appfile, $p) ? "Registered" : "Failed";
				echosys("\tFound Bundle : ".ucfirst($appfile)." ($state)", ($state=="Registered") ? MSG_NOTICE : MSG_ERROR);
			}else{
				if (substr($appfile,-4)==".php") {
					$state = self::RegisterApp($appfile, $p) ? "Registered" : "Failed";
					echosys("\tFound App : ".ucfirst(substr($appfile,0,-4)). " ($state)", ($state=="Registered") ? MSG_NOTICE : MSG_ERROR);
				}
			}
		}
	}

	public static function RegisterApp($Filename, $Path=null) {
		global $conf;
		if (!$Path) {
			$Path = $Filename;
			$Filename = basename($Filename);
			$Path = substr($Path, 0, -strlen($Filename));
		}
		$appname = ucfirst(substr($Filename,0,-4));
		if (!file_exists($Path.$Filename)) return false;
		include_once($Path.$Filename);
		$conffile = substr($Path.$Filename,0,-4).".conf";
		
		if (file_exists($conffile)) $conf = parseCfgFile($conffile, $conf);

		if (class_exists($appname)) {
			self::$Paths[$appname] = $Path;
			return self::RegisterClass($appname);
		} else { 
			echosys("Class ($appname) doesnt exist!", MSG_ERROR);
			return false;
		}
	}
	
	public static function getPath($appname) {
		$appname = ucfirst(strtolower($appname));
		return self::$Paths[$appname];
	}
	
	public static function getURL($appname) {
		$realpath = self::getPath($appname);
		$relpath = substr($realpath, strlen(rootpath()),-1);
		return root().$relpath;
	}

	public static function RegisterBundle($BundleName, $Path) {
		global $conf;
		$appname = ucfirst($BundleName);
		if (!file_exists($Path.$BundleName."/".$BundleName.".php")) return false;
		include_once($Path.$BundleName."/".$BundleName.".php");
		if (class_exists($appname)) {
			$conffile = $Path.$BundleName."/".$BundleName.".conf";
			if (file_exists($conffile)) $conf = parseCfgFile($conffile, $conf);
			if ($regist = self::RegisterClass($appname)) {

				self::$Paths[$appname] = $Path.$BundleName."/";
				return $regist;
			}else{
				return false;
			}
		}else return false;
	}

	public static function _scall($class, $func, $params = array()) {
		if (!is_callable(array($class, $func))) return "notcallable";
		return call_user_func_array(array($class, $func), $params);
	}

	public static function RegisterClass($ClassName) {
		global $conf;
		echosys(" - Registering Application Class ($ClassName)", MSG_NOTICE);
		if (self::_scall($ClassName, "_Type")!="Application") {
			echosys(" -- Class ($ClassName) is not an Application (try to extend it to 'App' class)", MSG_ERROR);
			return false;
		}

		$registerable = self::_scall($ClassName, "beforeRegister");
		if ($registerable===false) {
			echosys("Application ($ClassName) returned false", MSG_WARNING);
			return false;
		}

		/* SEARCH CLASS FUNCTIONS AND ADD THEM TO AVAILABLE CALLS */
		$methods = get_class_methods($ClassName);
		$uclass = ucfirst(strtolower($ClassName));
		$block = conf(strtolower($ClassName).".block");
		$unblock = conf(strtolower($ClassName).".unblock");
		// scan
//		if ($conf[strtolower($ClassName)]["block"]!="*"||$conf[strtolower($ClassName)]["unblock"]) {
			echosys("APP: Searching Methods for $ClassName Application", MSG_NOTICE);
			foreach ($methods as $method) {
				// Skip if blocked in conf.
				$lmethod = strtolower($method);
				$type = strpos($lmethod, "__")===false ? 
							'regular' : 
							substr($lmethod, 0, strpos($lmethod, "__"));
				$jmethod = strpos($method, "__")===false ? $lmethod : substr($lmethod, strpos($method, "__")+2);

				if ($block) {
					$saveit = false;
					echo 'HEY!!';
					// BLOCK IT 
					if ($block=="*") $saveit = false;
					if ($block==$lmethod || (is_array($block) && in_array($lmethod, $block))) $saveit = false;
					if ($block==$type."*" || (is_array($block) && in_array($type."*", $block))) $saveit = false;
					if ($block=="*".$jmethod) $saveit = false;
					if (is_array($block)&&in_array("*".$jmethod, $block)) $saveit = false;
					
					// SAVE IT 
					if ($unblock && is_array($unblock) && in_array($lmethod, $unblock)) $saveit = true;
					if ($unblock && $unblock==$lmethod) $saveit = true;
					
					if (is_array($unblock) && in_array($type."*", $unblock)) $saveit = true;
					if ($unblock==$type."*") $saveit = true;

					if ($unblock=="*".$jmethod) $saveit = true;
					if (is_array($unblock)&&in_array("*".$jmethod, $unblock)) $saveit = true;
					
					if ($saveit==false) continue;
				}

				// Check for internal blockage
				if (!in_array($method, self::$BlockedMethodNames)) {
					$bpos = strpos($method, "__");
					if ($bpos!==false&&$bpos!=0) {	// There is a calltype identifier
						$methodname = ucfirst(substr($method,$bpos+2));
						echosys("APP : Found ".$type." method {$jmethod} ($method) in {$ClassName} Application", MSG_NOTICE);
						self::$Calls[$type][$uclass][ucfirst($jmethod)] = array($ClassName, $method);
						if (conf("output-destination.default-{$type}"))
							$target = conf("output-destination.default-{$type}");
						elseif (conf("output-destination.".strtolower($ClassName)))
							$target = conf("output-destination.".strtolower($ClassName));
						else
				 			$target = conf("output-destination.default");
						self::$Targets[$type][$uclass][ucfirst($jmethod)] = $target;
					}else{		// no calltype, so 'regular'
						self::$Calls["regular"][$uclass][ucfirst(strtolower($method))] = array($ClassName, $method);
						
						if (!conf("output-destination.".strtolower($ClassName)))
							$target = conf("output-destination.default");
						else
							$target = conf("output-destination.".strtolower($ClassName));
						
						self::$Targets["regular"][$uclass][ucfirst(strtolower($method))] = $target;
						echosys("APP : Found regular method $method in $ClassName Application", MSG_NOTICE);
					}
				}
			}

		// set defaults
		// add to apps
		self::$Apps[$ClassName] = "Application";
		return true;
	}

	public static function Check($App, $Call = null, $CallType = "regular") {
		$App = ucfirst(strtolower($App));	$Call = ucfirst(strtolower($Call));
		echosys("Checking for app '$App::$Call'", MSG_NOTICE);

		if (!$Call) return (self::$Calls[$CallType][$App]) ? true : false;
		return self::$Calls[$CallType][$App][$Call] ? true : false;
	}
	
	public static function CallSeq($App, $Call, $Params, $CallType = "regular", $Output = null, $Method = 'append') {
		$App = ucfirst(strtolower($App));	$Call = ucfirst(strtolower($Call));

		// PRERUN OF SAME APPLICATION
		self::Call($App, "Prerun", array($Call, $Params), $CallType, $Output);

		// BEFORE__app_call ON ALL APPLICATIONS
		foreach (self::getMatch(self::$Calls['before'], $App, $Call) as $bc) 
			self::Call($bc['a'], $bc['c'], $Params, 'before', $Output);
			
		if (!self::$DoNotRun) $data = self::Call($App, $Call, $Params, $CallType, $Output, $Method);
		
		// AFTER__app_call ON ALL APPLICATIONS
		foreach (self::getMatch(self::$Calls['after'], $App, $Call) as $bc)
			self::Call($bc['a'], $bc['c'], $Params, 'after', $Output);
		
		// AFTERRUN OF SAME APPLICATION
		self::Call($App, "Afterrun", array($Call, $Params), $CallType, $Output);
		
		return $data;
	}
	
	private static function getMatch($Array, $App, $Call) {
		$ret = array();
		if (!is_array($Array)) return $ret;
		foreach ($Array as $_appn => $_app) {
			foreach ($_app as $_call => $_carr) {
				if (
					strtolower($_call)==strtolower($App."_".$Call) or
					strtolower($_call)==strtolower("x_".$Call) or
					strtolower($_call)==strtolower($App)
					)
				$ret[] = array('a'=>$_appn, 'c'=>$_call, '2c'=>$_carr);
			}
		}
		return $ret;
	}
	
	public static function GetCalls ($CallType, $App="*") {
		$ret = array();
		if ($App=="*")
			if (is_array(App::$Calls[$CallType]))
				foreach (App::$Calls[$CallType] as $app => $calls)
					foreach ($calls as $call => $tocall)
						$ret[] = array("app"=>$app, "call"=>$call, "tocall"=>$tocall);
		else
			if (is_array(App::$Calls[$CallType][ucfirst(strtolower($App))]))
				foreach (App::$Calls[$CallType][ucfirst(strtolower($App))] as $call => $tocall)
					$ret[] = array("app"=>ucfirst(strtolower($App)), "call"=>$call, "tocall"=>$tocall);

		return $ret;
	}
	
	public static function PreventOutput () {
		$GLOBALS['no-output'] = true;
	}
	
	public static function PreventRunning () {
		// THIS IS GOING TO BE DONE
		self::$DoNotRun = true;
	}
	
	/**
	 * do an Application call
	 */
	public static function Call($App, $Call, $Params, $CallType = "regular", $Output = null, $Method = 'append') {
		global $conf;
		static $depth = 0;
		$App = ucfirst(strtolower($App));	$Call = ucfirst(strtolower($Call));
		// $Output may be 'tpl' (template), 'std' (echo), 'ret' (return), 'plc' (place), 'page'
		// ob_end_flush(); echo "\ncall:$App,$Call";
		$callto = self::$Calls[$CallType][$App][$Call];
		if (!$callto) return echosys("Cannot Find $App :: $Call ($CallType)", MSG_WARNING);

		if (!is_callable($callto)) return false;
		
		if (!$Output) $Output = self::$Targets[$CallType][$App][$Call];
		
		if ($depth==0) Page::Set($conf["output-destination"]["debug"], ob_get_contents());

		if ($depth==0) ob_end_clean();
		
		//if (($depth==0)&&($Output!="std")) ob_start();
		if ($Output!="std") ob_start();
		
		$obforce = false;
		if ($GLOBALS['no-output']) $obforce = true;
		if ($obforce) ob_start();
		$depth++;
		echosys(strn(" =>", $depth)." Ran $App :: $Call ($CallType) ", MSG_NOTICE);
		
		Page::Set("ap", self::GetURL($callto[0]), "set");
		
		$data['return'] = call_user_func_array($callto, $Params);
		$depth--;
		
		//if (($depth==0)&&($Output!="std")) {
		if ($Output!="std") {
			$data['stdout'] = ob_get_contents();
			ob_end_clean();
			}
		
		if ($depth==0) if ($obforce) ob_end_clean();
		if ($depth==0) ob_start();
		
		if ($Output!="return") {
			$OMethod = self::$Methods[$CallType][$App][$Call] ? self::$Methods[$CallType][$App][$Call] : $Method;
			if ($Output!="std") Page::Set($Output, $data['stdout'], $OMethod);
		}
		
		self::$Returns[$CallType][$App][$Call] = $data['return'];
		return $data;
	}
	
	public static function getReturn($App, $Call, $CallType) {
		$App = ucfirst($App);	$Call = ucfirst($Call);	
		return self::$Returns[$CallType][$App][$Call];
	}

	public static function setPath ($Class, $Path) {
		self::$Paths[ucfirst(strtolower($Class))] = $Path;
	}
	
	public static function SetTarget($Target = null, $Calls = "*", $Method = 'append', $CallType = 'regular', $app = '__trace') {
		if ($app=='__trace') {
			$backtrace = debug_trace(1);
			$app = ucfirst(strtolower($backtrace['class']));
		}
		
		// PARSE CALLS
		//echo $app;
		//print_r(self::$Calls[$CallType]);
		if ($Calls=='*')
			$Calls = array_keys(self::$Calls[$CallType][$app]);
		else
			$Calls = parse_relaxarray($Calls);
		
		// REGISTER ALL
		foreach ($Calls as $_call) {
			$_call = ucfirst(strtolower($_call));
			self::$Targets[$CallType][$app][$_call] = $Target;
			self::$Methods[$CallType][$app][$_call] = $Method;
		}
	}

	/*******
	 * Functions for extender classes
	 *******/

	public static function _Type() {
		return 'Application';
	}

	public static function _Desc() {
		return 'Application Description, please override this function';
	}

}

?>